home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / utility / wzun11sr.zip / STATUS.C < prev    next >
C/C++ Source or Header  |  1992-04-18  |  22KB  |  668 lines

  1. /* Status.c -- the status module of WizUnzip
  2.  * Robert Heath. 1991.
  3.  */
  4.  
  5. #include <sys\types.h>
  6. #include <sys\stat.h>
  7. #include <time.h>                
  8. #include <windows.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include <io.h>
  12. #include <stdio.h>
  13. #include <stdarg.h>
  14. #include <assert.h>
  15. #include "wizunzip.h"
  16. #include "unzip.h"
  17.  
  18. #define STATUS_INCREMENT    512    /* incremental status data size        */
  19. #define MAX_H_CHARS 160            /* max horizontal chars.            */
  20.  
  21. #define MAX_INDEX_ENTRIES 16    /* Message Window index max entries    */
  22.  
  23. #define MAX_BUFFER_SIZE 0xffffL    /* max Message Buffer size. Must fit 
  24.                                  * within one memory segment!        */
  25.  
  26. #define MAX_TEXTOUT_COUNT 0x7fffL /* max no. bytes TextOut() accepts */
  27.  
  28. #define STDIO_BUF_SIZE (FILNAMSIZ+LONG_FORM_FNAME_INX) /* buffer size during printf or fprintf */
  29.  
  30. static short yClient;             /* height of client area            */
  31. static short yChar;                /* height of typical char.            */
  32. static short nVscrollPos = 0;    /* scroll position of mesg. window    */
  33. static short nNumLines = 0;            /* number of lines in buffer    */
  34. static short nVscrollMax;    /* max scroll position of mesg. window    */
  35. static DWORD dwStatusSize = 0L;        /* status data size             */
  36. static DWORD dwBufferSize = 0L;        /* Status buffer size.  Never 
  37.                                        exceeds MAX_BUFFER_SIZE        */
  38. static HANDLE hGlobalStatus;        /* global mesg. handle            */
  39. static DWORD dwMsgWinIndex[MAX_INDEX_ENTRIES]; /* max index entries    */
  40. static short    nIndexEntries;            /* no. active index entries, with
  41.                                        MAX_INDEX_ENTRIES as its max. 
  42.                                        When set to 0, it's time to 
  43.                                        re-index.*/
  44.  
  45. static short nLinesPerEntry;         /* lines per index entry    */
  46.  
  47. /* displayed when buffer shouldn't grow or can't grow                    */
  48. static char CLEARING_MSG[] =     
  49.             "Clearing Messages window to make room for more information.";
  50.  
  51.  
  52. static struct KeyEntry {
  53.     WORD    wVirtKey;
  54.     BOOL    bCntl;
  55.     int    iMessage;
  56.     WORD    wRequest;
  57.     } KeyTable[] = {
  58.     /* vertical scroll control
  59.      */
  60.     {VK_HOME,    TRUE,    WM_VSCROLL,    SB_TOP },
  61.     {VK_END,    TRUE,    WM_VSCROLL,    SB_BOTTOM },
  62.     {VK_PRIOR,    FALSE,    WM_VSCROLL,    SB_PAGEUP },
  63.     {VK_NEXT,    FALSE,    WM_VSCROLL,    SB_PAGEDOWN },
  64.     {VK_UP,        FALSE,    WM_VSCROLL,    SB_LINEUP },
  65.     {VK_DOWN,    FALSE,    WM_VSCROLL,    SB_LINEDOWN },
  66.  
  67.     /* horizontal scroll control
  68.      */
  69.     {VK_HOME,    FALSE,    WM_HSCROLL,    SB_TOP },
  70.     {VK_END,    FALSE,    WM_HSCROLL,    SB_BOTTOM },
  71.     {VK_PRIOR,    TRUE,    WM_HSCROLL,    SB_PAGEUP },
  72.     {VK_NEXT,    TRUE,    WM_HSCROLL,    SB_PAGEDOWN },
  73.     {VK_LEFT,    FALSE,    WM_HSCROLL,    SB_LINEUP },
  74.     {VK_RIGHT,    FALSE,    WM_HSCROLL,    SB_LINEDOWN },
  75.     } ;
  76.  
  77. #define NUMKEYS (sizeof(KeyTable)/sizeof(struct KeyEntry)) 
  78.  
  79. /* Forward Refs
  80.  */
  81. static void FreeStatusLog(void);
  82.  
  83. /* Globals
  84.  */
  85. BOOL bRealTimeMsgUpdate = TRUE; /* update messages window in real-time.
  86.                                  * Reset by callers when update can be
  87.                                  * be deferred.
  88.                                  */
  89.  
  90. /* Clears status buffer. Frees buffer.
  91.  */
  92. static void FreeStatusLog(void)
  93. {
  94.     if (hGlobalStatus)
  95.     {
  96.         GlobalFree(hGlobalStatus);
  97.         hGlobalStatus = NULL;
  98.     }
  99.     dwStatusSize = 0L;        /* status data size             */
  100.     dwBufferSize = 0L;        /* status buffer size             */
  101.     nNumLines = 0;            /* number of lines in buffer    */
  102.     nVscrollMax = 1;
  103.     SetScrollRange(hWndStatus, SB_VERT, 0, 1, FALSE);
  104.     nVscrollPos = 0;
  105.     SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  106. }
  107.  
  108. /* Update Message Window Position is called after adding 
  109.  * a number of lines to the message window without updating it.
  110.  * The function invalidates then updates the window.
  111.  */
  112. void UpdateMsgWndPos(void)
  113. {
  114.         nVscrollPos = max(0,(nNumLines-MessageWinLines+1));     /* set position to next to last line    */
  115.         SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  116.         InvalidateRect(hWndStatus, NULL, TRUE);
  117.         UpdateWindow(hWndStatus);
  118. }
  119.  
  120. /* Add message line (or part of a line) to the global status buffer
  121.  * that is the contents of the Message Window.
  122.  * Assumes that global data is unlocked when called.
  123.  */
  124. void WriteStringToMsgWin(PSTR String, BOOL bUpdate)
  125. {
  126.     WriteBufferToMsgWin(String, strlen(String), bUpdate);
  127. }
  128.  
  129. /* Add message buffer (maybe part of a line) to the global status buffer
  130.  * that is the contents of the Message Window.
  131.  * Assumes that global data is unlocked when called.
  132.  */
  133. void WriteBufferToMsgWin(LPSTR buffer, int nBufferLen, BOOL bUpdate)
  134. {
  135. LPSTR    lpC;                /* pointer into buffer                            */
  136. HANDLE hGlobalStatusTmp;
  137. LPSTR lpGlobalBuffer;            /* pointer into global buffer                */
  138. DWORD dwNewSize = dwStatusSize + (DWORD)nBufferLen;
  139. int    nIncrLines = 0;                /* incremental lines in buffer            */
  140. int nIncompleteExistingLine = 0; /* add -1 if incomplete existing last line    */
  141. int nIncompleteAddedLine = 0;    /* add +1 if incomplete added last line        */
  142. DWORD dwRequestedSize;            /* Size needed to hold all data. Can't
  143.                                    practically exceeded MAX_BUFFER_SIZE.*/
  144.  
  145.     if (!nBufferLen)            /* if no data                            */
  146.         return;                    /* just beat it                            */
  147.  
  148.     /* count LF's in buffer    to later add to total                        */
  149.     for (lpC = buffer; lpC != NULL && (lpC - buffer) < nBufferLen; )
  150.     {
  151.         /* use memchr() for speed (?) considerations                    */
  152.         if (lpC = _fmemchr(lpC, '\n', (size_t)(nBufferLen - (lpC - buffer))))
  153.         {
  154.             nIncrLines++;            /* tally line found                    */
  155.             lpC++;                    /* point beyond LF for next pass     */
  156.         }
  157.     }
  158.     if (dwNewSize > dwBufferSize)    /* if won't fit or 1st time                */
  159.     {
  160.         /* Round up if necessary to nearest whole increment
  161.          */
  162.         dwRequestedSize = ((dwNewSize + STATUS_INCREMENT - 1) / 
  163.                             STATUS_INCREMENT) * STATUS_INCREMENT;
  164.         if (hGlobalStatus)    /* if buffer exists, realloc            */
  165.         {
  166.                 if (dwRequestedSize <= MAX_BUFFER_SIZE &&
  167.                         (hGlobalStatusTmp = GlobalReAlloc(hGlobalStatus,
  168.                                     dwRequestedSize, GMEM_MOVEABLE)))
  169.                 {
  170.                     /* successful re-allocation                        */
  171.                     hGlobalStatus = hGlobalStatusTmp;
  172.                     dwBufferSize = dwRequestedSize;
  173.                 }
  174.                 else /* re-allocation failed, make last-ditch attempt! */
  175.                 {
  176.                     FreeStatusLog();        /* free own buffers        */
  177.                     MessageBox (hMainWnd, CLEARING_MSG,
  178.                                 "Note", MB_ICONINFORMATION | MB_OK);
  179.                     WriteBufferToMsgWin(buffer, nBufferLen, bUpdate);
  180.                     return;
  181.                 }
  182.         }
  183.         else    /* 1st time                                            */
  184.         {
  185.                 if (hGlobalStatus = GlobalAlloc(GMEM_MOVEABLE,
  186.                                     dwRequestedSize))
  187.                 {
  188.                     dwBufferSize = dwRequestedSize;    /* save it        */
  189.                 }
  190.                 else    /* 1st allocation failed!                    */
  191.                 {
  192.                     WinAssert(hGlobalStatus);    /* DEBUG                 */
  193.                     return;
  194.                 }
  195.         }
  196.     }
  197.     /* should be easy copy of data from here                            */
  198.     lpGlobalBuffer = GlobalLock(hGlobalStatus);
  199.     if (lpGlobalBuffer)
  200.     {
  201.  
  202.         /* Account for partial lines existing and being added.
  203.          */
  204.         if (dwStatusSize  &&
  205.             lpGlobalBuffer[dwStatusSize-1] != '\n')
  206.                 nIncompleteExistingLine-- ;    /* subtract 1                    */
  207.  
  208.         if (buffer[nBufferLen-1] != '\n') /* nBufferLen guaranteed >0 */
  209.                 nIncompleteAddedLine++ ;  /* add 1                    */
  210.  
  211.         /* copy data into global buffer                            */
  212.         if (nBufferLen)      /* map to ANSI; if 0 don't copy; 0 means 65K    */
  213.         {
  214.             OemToAnsiBuff(buffer, &lpGlobalBuffer[dwStatusSize], nBufferLen);    
  215.         }
  216.         /* bump no. lines accounting for incomplete lines        */
  217.         nNumLines += (nIncrLines+nIncompleteExistingLine+nIncompleteAddedLine); 
  218.         dwStatusSize = dwNewSize;        /* new data size counting end null    */
  219.         GlobalUnlock(hGlobalStatus);
  220.         nVscrollMax = max(1, nNumLines + 2 - yClient/yChar);
  221.         SetScrollRange(hWndStatus, SB_VERT, 0, nVscrollMax, FALSE);
  222.         nIndexEntries = 0;    /* re-index whenever more data is added            */
  223.         if (bUpdate)        /* if requested to update message box            */
  224.         {
  225.             nVscrollPos = max(0,(nNumLines-MessageWinLines+1));     /* set position to next to last line    */
  226.             SetScrollPos(hWndStatus, SB_VERT, nVscrollPos, TRUE);
  227.             InvalidateRect(hWndStatus, NULL, TRUE);
  228.             UpdateWindow(hWndStatus);
  229.         }
  230.     }
  231.     else
  232.     {
  233.         WinAssert(lpGlobalBuffer);    /* DEBUG */
  234.     }
  235. }
  236.  
  237.  
  238.  
  239. long FAR PASCAL StatusProc (HWND hWnd, unsigned iMessage, WORD wParam, LONG lParam) 
  240. {
  241. static short xClient ;             /* size of client area    */
  242. HDC        hDC;                    /* device context            */
  243. TEXTMETRIC      tm;            /* text metric structure    */
  244. PAINTSTRUCT ps;
  245. #ifdef OLD_WAY
  246. RECT    rect;
  247. #endif
  248. struct KeyEntry *pKE;        /* pointer to key entry        */
  249. LPSTR    lpStatusBuffer;            /* pointer to global mesg. buffer        */
  250. int        nMenuItemCount;    /* no. items in System menu    before deleting separators    */
  251. BOOL    bCntl;                /* control shift pressed ?                */
  252. static short xChar;
  253. static short nHscrollMax;
  254. static short nHscrollPos;
  255. static short nMaxWidth;        /* in pixels                                */
  256.            short nVscrollInc;
  257.         short nHscrollInc;
  258. short i, x, y, nPaintBeg, nPaintEnd;
  259. HMENU    hSysMenu;            /* this guy's system menu                    */
  260.  
  261.     switch (iMessage) {
  262.     case WM_CREATE:
  263. #ifdef NEEDME
  264.         wStatusItem = LOWORD((LONG)(((LPCREATESTRUCT)lParam)->lpCreateParams));
  265. #endif
  266.         hDC = GetDC(hWnd);    /* get device context */
  267.           hOldFont   = SelectObject ( hDC, hFixedFont);
  268.         GetTextMetrics(hDC, &tm);
  269.         ReleaseDC(hWnd, hDC);
  270.         xChar = tm.tmAveCharWidth;
  271.         yChar = tm.tmHeight + tm.tmExternalLeading; 
  272.         nMaxWidth = MAX_H_CHARS * xChar;
  273.         nVscrollPos = 0;                    /* reset position to 0    */
  274.         nVscrollMax = max(1,nNumLines);
  275.         SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
  276.         SetScrollPos(hWnd, SB_VERT, 0, TRUE);
  277.         hSysMenu = GetSystemMenu(hWnd, FALSE);
  278.         DeleteMenu(hSysMenu, SC_SIZE, MF_BYCOMMAND); /* delete menu item */
  279.         DeleteMenu(hSysMenu, SC_MOVE, MF_BYCOMMAND); /* delete menu item */
  280.         DeleteMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND); /* delete menu item */
  281.         DeleteMenu(hSysMenu, SC_TASKLIST, MF_BYCOMMAND); /* delete menu item */
  282.         /* walk thru menu and delete all separator bars
  283.          */
  284.           for (nMenuItemCount = GetMenuItemCount(hMenu);
  285.             nMenuItemCount ; nMenuItemCount--)
  286.           {
  287.             if (GetMenuState(hSysMenu, nMenuItemCount-1, MF_BYPOSITION) & MF_SEPARATOR)
  288.             {
  289.                 DeleteMenu(hSysMenu, nMenuItemCount-1, MF_BYPOSITION);
  290.             }
  291.         }
  292.         return 0;
  293.  
  294.     case WM_SIZE:
  295.         xClient = LOWORD(lParam);/* x size of client area */
  296.         yClient = HIWORD(lParam);/* y size of client area */
  297.  
  298.         nVscrollMax = max(1, nNumLines + 2 - yClient/yChar);
  299.         nVscrollPos = min(nVscrollPos, nVscrollMax);
  300.  
  301.         SetScrollRange(hWnd, SB_VERT, 0, nVscrollMax, FALSE);
  302.         SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  303.  
  304.         nHscrollMax = max(0, 2 + (nMaxWidth - xClient) / xChar);
  305.         nHscrollPos = min(nHscrollPos, nHscrollMax);
  306.  
  307.         SetScrollRange(hWnd, SB_HORZ, 0, nHscrollMax, FALSE);
  308.         SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
  309.  
  310.         return 0;
  311.  
  312.     case WM_SYSCOMMAND:
  313.         switch ((wParam & 0xFFF0)) { /* decode parameter type    */
  314.         case SC_RESTORE:    /* alert parent            */
  315.                 PostMessage(hMainWnd, WM_COMMAND, 
  316.                     IDM_RESTORE_STATUS, 0L);
  317.             break;
  318.         case SC_MAXIMIZE:
  319.                 PostMessage(hMainWnd, WM_COMMAND,                      
  320.                         IDM_MAX_STATUS, 0L);
  321.             break;
  322.         default:
  323.             return (DefWindowProc(hWnd, iMessage, wParam, lParam));
  324.         }
  325.         break;
  326.     case WM_COMMAND:
  327.         switch (wParam) { /* decode parameter type    */
  328.         case IDM_CLEAR_STATUS:
  329.             FreeStatusLog();        /* free log contents        */
  330.             InvalidateRect(hWndStatus, NULL, TRUE);
  331.             break;
  332.         default:
  333.             break;
  334.         }
  335.         break;
  336.     case WM_VSCROLL:    /* scroll bar action on list box */
  337.         switch (wParam) { /* decode parameter type    */
  338.         case SB_TOP:
  339.             nVscrollInc = -nVscrollPos;
  340.             break;
  341.         case SB_BOTTOM:
  342.             nVscrollInc = nVscrollMax - nVscrollPos;
  343.             break;
  344.         case SB_LINEUP:
  345.             nVscrollInc = -1;
  346.             break;
  347.         case SB_LINEDOWN:
  348.             nVscrollInc = 1;
  349.             break;
  350.         case SB_PAGEUP:
  351.             nVscrollInc = min(-1, -yClient/yChar);
  352.             break;
  353.         case SB_PAGEDOWN:
  354.             nVscrollInc = max(1, yClient/yChar);
  355.             break;
  356.         case SB_THUMBPOSITION:
  357.             nVscrollInc = LOWORD(lParam) - nVscrollPos;
  358.             break;
  359.         default:    /* END_SCROLL comes thru here                */
  360.             nVscrollInc = 0;
  361.         } /* bottom of scroll-decoding switch    */
  362.  
  363.         if (nVscrollInc = max(-nVscrollPos,
  364.                                 min(nVscrollInc, nVscrollMax - nVscrollPos)))
  365.         {
  366.             nVscrollPos += nVscrollInc;
  367.             ScrollWindow(hWnd, 0, -yChar * nVscrollInc, NULL, NULL);
  368.             SetScrollPos(hWnd, SB_VERT, nVscrollPos, TRUE);
  369.             UpdateWindow(hWnd);
  370.         }
  371.         return 0;
  372.  
  373.     case WM_HSCROLL:    /* scroll bar action on list box */
  374.         switch (wParam) { /* decode parameter type    */
  375.         case SB_TOP:
  376.             nHscrollInc = -nHscrollPos;
  377.             break;
  378.         case SB_BOTTOM:
  379.             nHscrollInc = nHscrollMax - nHscrollPos;
  380.             break;
  381.         case SB_LINEUP:
  382.             nHscrollInc = -1;
  383.             break;
  384.         case SB_LINEDOWN:
  385.             nHscrollInc = 1;
  386.             break;
  387.         case SB_PAGEUP:
  388.             nHscrollInc = -8;
  389.             break;
  390.         case SB_PAGEDOWN:
  391.             nHscrollInc = 8;
  392.             break;
  393.         case SB_THUMBPOSITION:
  394.             nHscrollInc = LOWORD(lParam) - nHscrollPos;
  395.             break;
  396.         default:
  397. #ifdef CATCHING_END_SCROLL
  398.             nHscrollInc = 0;
  399. #else
  400.             return (DefWindowProc(hWnd, iMessage, wParam, lParam));
  401. #endif
  402.         } /* bottom of scroll-decoding switch    */
  403.  
  404.         if (nHscrollInc = max(-nHscrollPos,
  405.                                 min(nHscrollInc, nHscrollMax - nHscrollPos)))
  406.         {
  407.             nHscrollPos += nHscrollInc;
  408.             ScrollWindow(hWnd, -xChar * nHscrollInc, 0, NULL, NULL);
  409.             SetScrollPos(hWnd, SB_HORZ, nHscrollPos, TRUE);
  410.         }
  411.         return 0;
  412.  
  413.     case WM_KEYDOWN:
  414.         bCntl = (BOOL)(GetKeyState(VK_CONTROL) < 0 ? TRUE : FALSE);
  415.         for (i = 0, pKE = KeyTable; i < NUMKEYS; i++, pKE++)
  416.         {
  417.             if (wParam == pKE->wVirtKey && bCntl == pKE->bCntl)
  418.             {
  419.                 SendMessage(hWnd, pKE->iMessage, pKE->wRequest, 0L);
  420.                 break;
  421.             }
  422.         }
  423.         break;
  424.     case WM_PAINT:
  425.         if (!hGlobalStatus)         /* if nothing to paint            */
  426.         {
  427.             return (DefWindowProc(hWnd, iMessage, wParam, lParam)); /* exit */
  428.         }
  429.         {
  430.         REGISTER LPSTR lpC;            /* current char                    */
  431.                 LPSTR lpCtemp;        /* address of next '\n' in buffer */
  432.                 LPSTR lpStartC;     /* paint starting character        */
  433.                 LPSTR lpBegLine;    /* beginning of current line    */
  434.                 HANDLE hNewHandle;
  435.                 DWORD    dwLineLen;    /* length of current line        */
  436.                 short  nLinesSinceLastEntry; /* lines since last entry */
  437.                 DWORD  dwSearchLen;    /* length for _fmemchr() to search */
  438.  
  439.             lpStartC = NULL; /* paint starting character        */
  440.             lpStatusBuffer = GlobalLock(hGlobalStatus);
  441.             WinAssert(lpStatusBuffer);    /* DEBUG */
  442.             hDC = BeginPaint(hWnd, &ps);
  443.             WinAssert(hDC);                /* DEBUG */
  444.               hNewHandle = SelectObject ( hDC, hFixedFont);
  445.             WinAssert(hNewHandle);    /* DEBUG */
  446.             nPaintBeg = max(0, nVscrollPos+ps.rcPaint.top/yChar -1);
  447.             nPaintEnd = min(nNumLines, nVscrollPos + ps.rcPaint.bottom/yChar);
  448.             if (nPaintBeg >= nPaintEnd)    /* if no painting to do ...        */
  449.             {
  450.                 EndPaint(hWnd, &ps);    /* just exit                    */
  451.                 GlobalUnlock(hGlobalStatus);    /* unlock memory        */
  452.                 return 0;
  453.             }
  454.             if (!nIndexEntries)    /* re-index whenever more data is added    */
  455.             {
  456.                 /* Round up to make lines_per_entry encompass all
  457.                  * possible lines in buffer.
  458.                  */
  459.                 nLinesPerEntry = (nNumLines+MAX_INDEX_ENTRIES-1) / MAX_INDEX_ENTRIES; 
  460.                 if (!nLinesPerEntry)    /* if zero                    */ 
  461.                     nLinesPerEntry++;    /* set to 1 as minimum        */ 
  462.  
  463.                 /* Count lines from beginning of buffer to:
  464.                  * 1) mark beginning of paint sequence (lpStartC) and
  465.                   * 2) periodically save buffer index in MsgWinIndex[] table.
  466.                  */
  467.                 for (lpC = lpStatusBuffer, i = 0, nLinesSinceLastEntry = 0;
  468.                      (DWORD)(lpC - lpStatusBuffer) < dwStatusSize ; i++)
  469.                 {
  470.                     /* We are at the 1st character position in the line  */
  471.                     if (i == nPaintBeg) /* Starting point for paint ? */
  472.                         lpStartC = lpC;    /* If so, mark starting point */
  473.  
  474.                     /* Entry time ? */
  475.                     if (!nLinesSinceLastEntry++ && 
  476.                          nIndexEntries < MAX_INDEX_ENTRIES)
  477.                     {
  478.                         dwMsgWinIndex[nIndexEntries] = 
  479.                                 (DWORD)(lpC - lpStatusBuffer); /* save index */
  480.                         nIndexEntries++;
  481.                     }
  482.                     if (nLinesSinceLastEntry >= nLinesPerEntry)
  483.                         nLinesSinceLastEntry = 0;
  484.  
  485.                     /* Use _fmemchr() to find next LF.  
  486.                      * It's probably optimized for searches.
  487.                      */
  488.                     dwSearchLen = dwStatusSize - 
  489.                                         (DWORD)(lpC - lpStatusBuffer);
  490.                     if ((lpCtemp = _fmemchr(lpC, '\n', (size_t)dwSearchLen)))
  491.                         lpC = ++lpCtemp;    /* use next char as beg of line */
  492.  
  493.                     else /* else use lpC with incremented value            */
  494.                         lpC += dwSearchLen;
  495.  
  496.                 } /* bottom of still-counting-lines loop                */
  497.                 WinAssert(lpStartC);    /* DEBUG                             */
  498.                 lpC = lpStartC;        /* restore starting point            */
  499.                 WinAssert((DWORD)lpC >= (DWORD)lpStatusBuffer &&    /* DEBUG */
  500.                    (DWORD)lpC < (DWORD)&lpStatusBuffer[dwStatusSize]); 
  501.  
  502.             }     /* bottom of need-to-build-index block                    */
  503.             else    /* index is still valid                                */
  504.             {
  505.             short nIndexEntry;                     /* work backwards!        */
  506.  
  507.                 /* Find index of line number which is equal to or just
  508.                  * below the starting line to paint. Work backwards
  509.                  * thru the table. Here, "i" is the line no. corresponding
  510.                  * to the current table index.
  511.                  */
  512.                 for (nIndexEntry = nIndexEntries - 1,
  513.                      i = nIndexEntry * nLinesPerEntry; 
  514.                         nIndexEntry >= 0 && 
  515.                         nPaintBeg < i ;
  516.                         nIndexEntry--, i -= nLinesPerEntry )
  517.                     ;
  518.  
  519.                 WinAssert(nIndexEntry >= 0);    /* DEBUG */
  520.                 WinAssert(i <= nPaintBeg);        /* DEBUG */
  521.                 /* OK, we've got a head start on the search.
  522.                  * Start checking characters from the position found
  523.                  * in the index table.
  524.                  */
  525.                 for (lpC = &lpStatusBuffer[dwMsgWinIndex[nIndexEntry]];
  526.                      i < nPaintBeg && 
  527. #ifndef NEW_WAY
  528.                         (DWORD)(lpC - lpStatusBuffer) < dwStatusSize;
  529. #else
  530.                         ((DWORD)lpC - (DWORD)lpStatusBuffer) < dwStatusSize;
  531. #endif
  532.                      i++)
  533.                 {
  534.                     /* Find length of current line.  Use _fmemchr() to 
  535.                      * find next LF.  
  536.                      */
  537.                     dwSearchLen = dwStatusSize - 
  538.                                     (DWORD)(lpC - lpStatusBuffer);
  539.                     if ((lpCtemp = _fmemchr(lpC, '\n', (size_t)dwSearchLen)) != NULL)
  540.                         lpC = ++lpCtemp; /* point to next char. past '\n'    */
  541.  
  542.                     else /* If search fails, pretend LF exists, go past it.    */
  543.                         lpC += dwSearchLen;
  544.  
  545.                 }
  546.             } /* bottom of index-is-still-valid block.                    */
  547.  
  548.             /* At this point we've got the buffer address, lpC, for the 1st
  549.              * line at which we begin painting, nPaintBeg.
  550.              */
  551.             for (i = nPaintBeg; 
  552.                  i < nPaintEnd && 
  553. #ifndef NEW_WAY
  554.                  (DWORD)(lpC  - lpStatusBuffer) >=  0L &&    
  555.                  (DWORD)(lpC  - lpStatusBuffer) < dwStatusSize ; 
  556. #else
  557.                  ((DWORD)lpC  - (DWORD)lpStatusBuffer) >=  0L &&    
  558.                  ((DWORD)lpC  - (DWORD)lpStatusBuffer) < dwStatusSize ; 
  559. #endif
  560.                  i++)
  561.             {
  562.                 lpBegLine = lpC;
  563.                 /* Find length of current line. Use _fmemchr() to find next LF.
  564.                  */
  565.                 dwSearchLen = dwStatusSize - (DWORD)(lpC - lpStatusBuffer);
  566.                 if ((lpCtemp = _fmemchr(lpC, '\n', (size_t)dwSearchLen)) == NULL)
  567.                 {
  568.                     /* If search fails, pretend we found LF, we won't 
  569.                      * display it anyway.
  570.                      */
  571.                     lpCtemp  = lpC + dwSearchLen;
  572.                 }
  573.                 WinAssert((DWORD)lpCtemp >= (DWORD)lpBegLine);    /* should be non-negative    */
  574.                 WinAssert((DWORD)lpCtemp >= (DWORD)lpStatusBuffer &&    /* DEBUG */
  575.                    (DWORD)lpCtemp <= (DWORD)&lpStatusBuffer[dwStatusSize]); 
  576.  
  577.                 x = xChar * (1 - nHscrollPos);
  578.                 y = yChar * (1 - nVscrollPos + i);
  579.                 dwLineLen = (DWORD)(lpCtemp - lpBegLine);/* calc length*/
  580.                 if (dwLineLen && lpBegLine[dwLineLen-1] == '\r')
  581.                     dwLineLen--;     /* don't display '\r'    */
  582.  
  583.                 /* may be displaying long lines if binary file        */
  584.                 if (dwLineLen > MAX_TEXTOUT_COUNT) 
  585.                     dwLineLen = MAX_TEXTOUT_COUNT;
  586.  
  587.                 TabbedTextOut(hDC, x, y, lpBegLine, (int)dwLineLen, 0, NULL, 0);
  588.                 lpC = ++lpCtemp; /* point to next char. past '\n'        */
  589.             }
  590.             EndPaint(hWnd, &ps);
  591.             GlobalUnlock(hGlobalStatus);    /* unlock memory        */
  592.             return 0;
  593.         }
  594.         return (DefWindowProc(hWnd, iMessage, wParam, lParam));
  595.         break;
  596.     case WM_CLOSE:              /* message: close the window    */
  597.         DestroyWindow(hWnd); /* close the mesg. window    */
  598.             break;
  599.     case WM_DESTROY:
  600.         FreeStatusLog();
  601.         break;
  602.     default:
  603.         return (DefWindowProc(hWnd, iMessage, wParam, lParam));
  604.     }
  605.     return 0L;
  606. }
  607.  
  608. /* Printf buffers the current output and counts the number of lines
  609.  * within it.  It makes sure there is enough space in the global
  610.  * buffer, then copies the buffered data to the global buffer.
  611.  * It then triggers a repaint of the status buffer.
  612.  */
  613. int _FAR_ _cdecl printf(const char _FAR_ *format, ...)
  614. {
  615. va_list argptr;
  616. int    LinesInBuffer = 0;
  617. HANDLE hMemory;
  618. PSTR psBuffer;
  619.  
  620.     va_start(argptr, format);
  621.     hMemory = LocalAlloc(LMEM_MOVEABLE, STDIO_BUF_SIZE);
  622.     WinAssert(hMemory);
  623.     if (!hMemory)
  624.     {
  625.         return 0;
  626.     }
  627.     psBuffer = (PSTR)LocalLock(hMemory);
  628.     WinAssert(psBuffer);    /* DEBUG */
  629.     vsprintf(psBuffer, format, argptr);
  630.     va_end(argptr);    
  631.     WinAssert(strlen(psBuffer) < STDIO_BUF_SIZE);    /* raise STDIO_BUF_SIZE ?    */
  632.     WriteStringToMsgWin(psBuffer, bRealTimeMsgUpdate);
  633.       LocalUnlock(hMemory);                 /* unlock it to Windows */
  634.       LocalFree(hMemory);                 /* Returns it to Windows */
  635.     return 0;
  636. }
  637.  
  638. /* fprintf clone for code in unzip.c, etc. 
  639.  */
  640. int _FAR_ _cdecl fprintf(FILE _FAR_ *file, const char _FAR_ *format, ...)
  641. {
  642. va_list argptr;
  643. int    LinesInBuffer = 0;
  644. HANDLE hMemory;
  645. PSTR psBuffer;
  646.  
  647.     va_start(argptr, format);
  648.     hMemory = LocalAlloc(LMEM_MOVEABLE, STDIO_BUF_SIZE);
  649.     WinAssert(hMemory);
  650.     if (!hMemory)
  651.     {
  652.         return 0;
  653.     }
  654.     psBuffer = (PSTR)LocalLock(hMemory);
  655.     WinAssert(psBuffer);    /* DEBUG */
  656.     vsprintf(psBuffer, format, argptr);
  657.     va_end(argptr);    
  658.     WinAssert(strlen(psBuffer) < STDIO_BUF_SIZE);    /* raise STDIO_BUF_SIZE ?    */
  659.     WriteStringToMsgWin(psBuffer, bRealTimeMsgUpdate);
  660.       LocalUnlock(hMemory);                 /* unlock it to Windows */
  661.       LocalFree(hMemory);                 /* Returns it to Windows */
  662. }
  663.  
  664. void _FAR_ _cdecl perror(const char _FAR_ *parm1)
  665. {
  666.     printf(parm1);
  667. }
  668.